package marco.android.CouchControl;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import marco.android.CouchControl.R;

public class TCPClient extends Connection
{
	private static final String TAG = "TCPClient";
	private String mIP = "";
	
	public TCPClient()
	{
		try
		{
			mIP = getLocalIpAddress();
			Logs.d(TAG, mIP);
		}
		catch(SocketException e)
		{
			Logs.d(TAG, e);
		}
	}
	
	@Override
	public void Initialize(String address, Integer port)
	{
		mServerAddress = address;
		mPort = port;
	}
	
	public void Start()
	{
		mConnectionThread = new ConnectionThread("CouchControll::WiFi");
		mConnectionThread.start();
	}
	
	public void Stop()
	{
		if(mConnectionThread != null)
		{
			mConnectionThread.interrupt();
		}
	}

	public class ConnectionThread extends Thread
	{
		Socket mSocket = null; // new Socket();
		
		public ConnectionThread(String name)
		{
			super(name);
		}
		
		private int SendEvent(Event e)
		{
			mByteStream.reset();
		    mNetIO.EventToByteArray(e);

		    try
		    {
			   mSocket.getOutputStream().write(mByteStream.toByteArray(), 0, mByteStream.size());
			   mSocket.getOutputStream().flush();
		    }
		    catch(IOException ex)
		    {
		    	return(-1);
		    }
			return(mByteStream.size());
		}
		
		
		public int Stop(Socket s)
		{
			if(s != null)
			{
				try
				{
					Event e = new Event();
					e.Source = 'E';
					SendEvent(e);
					s.close();
				}
				catch(IOException e)
				{
					return -1;
				}
			}

			return 0;
		}
		
		@Override
		public void run()
		{			
			while(!isInterrupted())
			{
				try
				{
					mListener.UpdateConnectionStatus("Connecting to: " + mServerAddress + ":" + String.valueOf(mPort) + " from: " + mIP);
					//SocketAddress sa = new InetSocketAddress(mServerAddress, mPort);
					mSocket = new Socket(mServerAddress, mPort);
					//mSocket.connect(sa, 500);
					if(!mSocket.isConnected())
					{
						// Attempt to re-connect
						try
						{
						   Thread.sleep(500);
						}
						catch(InterruptedException e)
						{
							Logs.e(TAG, e);
							return;
						}
						continue;
					}
					else
					{
						mListener.UpdateConnectionStatus("Connected to: " + mServerAddress + ":" + String.valueOf(mPort) + " from: " + mIP);
						while(!isInterrupted())
						{
							synchronized(sLock)
							{
								try
								{
									sLock.wait();
									if(SendEvent(mEvent) < 0)
									{
										// There was an issue sending data
										// Try to re-connect
										mListener.UpdateConnectionStatus("Error writing to: " + mServerAddress + ":" + String.valueOf(mPort) + " from: " + mIP);
										sLock.notifyAll();
										break;
									}
								}
								catch(InterruptedException e)
								{
									throw(e);
								}
							}
						}
					}
				}
				catch(InterruptedException e)
				{
					mEvent.Source = 'E';
					SendEvent(mEvent);
					Logs.e(TAG, e);
					CloseSocket();
					return;
				}
				catch(UnknownHostException e)
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				catch(IOException e)
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				finally
				{
					CloseSocket();
					//sLock.notifyAll();
				}
			}
			Logs.d(TAG, "Exit thread");
		}
		
		private void CloseSocket()
		{
			if(mSocket != null)
			{
				try
				{
					mSocket.close();
				}
				catch(IOException e)
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				mSocket = null;
			}
		}
	}
	
	static public String readTCPSocket(Socket s, String data) throws IOException
	{
		BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
		return(in.readLine());
//		char [] d = new char[s.getReceiveBufferSize()];
//		in.read(d, 0, s.getReceiveBufferSize());
//		data = String.valueOf(d);
	}
	
	static public void writeTCPSocket(Socket s, String data) throws IOException
	{
		PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(s.getOutputStream())), true);
		out.println(data);
	}
	
	static public String getLocalIpAddress() throws SocketException
	{
		for(Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();)
		{
			NetworkInterface intf = en.nextElement();
			for(Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();)
			{
				InetAddress inetAddress = enumIpAddr.nextElement();
				if(!inetAddress.isLoopbackAddress())
				{
					return inetAddress.getHostAddress().toString();
				}
			}
		}
		return null;
	}
	
	public Connection.IOMode GetMode()
	{
		return(Connection.IOMode.eWiFi);
	}
}
